//
//  YWScanFaceController.m
//  YWPhotoEditMaster
//
//  Created by jun peng on 2023/9/7.
//

#import "YWScanFaceController.h"
#import <AVFoundation/AVFoundation.h>

@interface YWScanFaceController ()<AVCaptureMetadataOutputObjectsDelegate,AVCapturePhotoCaptureDelegate>

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *toBottom;

@property (weak, nonatomic) IBOutlet UIView *scanBackView;

@property (weak, nonatomic) IBOutlet UIImageView *chongjiboImageView;

@property(nonatomic,strong)AVCaptureSession *session; //要把session指着保存下来

@property(nonatomic,weak)AVCaptureVideoPreviewLayer *layer; //绘框的时候需要用到图层所以要指着保存下来

@property (weak, nonatomic) IBOutlet UIView *iconBgView;

@property (weak, nonatomic) UIScrollView *iconScrollView;

@property (weak, nonatomic) IBOutlet UIView *captureBgView;

//（拍照部分）
//照片输出流
@property (nonatomic) AVCapturePhotoOutput *imageOutPut;


@end

@implementation YWScanFaceController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIScrollView *scrollView = [[UIScrollView alloc] init];
    [self.iconBgView addSubview:scrollView];
    self.iconScrollView = scrollView;
    
    
    //（拍照部分）
    //准备拍照
    [self prepareTakePhotoAuthorization];
    
    //开始扫描
    [self startScan];
    
    
}



//（拍照部分）
//开启拍照权限
-(void)prepareTakePhotoAuthorization{
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    
    if(authStatus != AVAuthorizationStatusAuthorized){
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
                    dispatch_sync(dispatch_get_main_queue(), ^{
                        if (granted) {
                           // 申请权限成功
                        } else {
                            // 申请权限失败
                        }
                    });
        }];
    }
}

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];
    
    self.iconScrollView.frame = self.iconBgView.bounds;
}

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    
    [self startScanAnimation];
    
}

-(void)startScanAnimation{
    
    self.toBottom.constant = self.scanBackView.height;
    [self.view layoutIfNeeded];
    self.toBottom.constant = -self.scanBackView.height;
    [UIView animateWithDuration:1 animations:^{
        
        [UIView setAnimationRepeatCount:MAXFLOAT];
        [self.view layoutIfNeeded];
    }];
    
}

-(void)startScan{
    
    //1、设置输入
    //1.1、获取摄像头设备
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    //1.2、把摄像头设备当做输入设备 (设备输入)
    NSError *error = nil;
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
    
    //2、设置输出 （元数据输出）
    AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
    [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
    //3、创建会话，连接输入和输出
    AVCaptureSession *session = [[AVCaptureSession alloc] init];
    if([session canAddInput:input] && [session canAddOutput:output]) {//不是所有的输入输出都能添加进来所以要判断
        
        //会话添加输入输出
        [session addInput:input];
        [session addOutput:output];
    }
    else{
        return;
    }
    self.session = session;
    
    //（拍照部分）
    //拿到的图像的大小可以自行设定
    if ([session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
        [session setSessionPreset:AVCaptureSessionPreset1280x720];
    }
    
    
    //3.1、设置二维码可以识别的码制 （放在output add在session后面、不然会崩）
//    output.metadataObjectTypes = output.availableMetadataObjectTypes; //这个是任何可以识别的东西都能识别
//    output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode]; // 二维码 //AVMetadataObjectTypeFace
    output.metadataObjectTypes = @[AVMetadataObjectTypeFace]; // 人面 //AVMetadataObjectTypeFace
    
    //设置扫描的兴趣区域
    //CGRectMake(0, 0, 1, 1) 0.0~1.0
    //它的坐标系是先把手机的头向左倒置地横向摆放、然后再以左上角为坐标（0,0）
    CGRect bounds = self.captureBgView.bounds;
    CGFloat x = self.scanBackView.x*1.0/bounds.size.width;
    CGFloat y = self.scanBackView.y*1.0/bounds.size.height;
    CGFloat w = self.scanBackView.width*1.0/bounds.size.width;
    CGFloat h = self.scanBackView.height*1.0/bounds.size.height;
    output.rectOfInterest = CGRectMake(y, x, h, w);
    
    
    //（拍照部分）
    //创建视频输出源 并添加到会话
    AVCapturePhotoOutput *imageOutPut = [[AVCapturePhotoOutput alloc]init];
    self.imageOutPut = imageOutPut;
        if ([session canAddOutput:imageOutPut]) {
            [session addOutput:imageOutPut];
        }
    AVCaptureConnection *imageConnection = [imageOutPut connectionWithMediaType:AVMediaTypeVideo];
    // 设置 imageConnection 控制相机拍摄图片的角度方向
    if (imageConnection.supportsVideoOrientation) {
        imageConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
    }
    
    
    //3.2、添加视频预览图层、让用户可以看到界面（但不是必须添加只是为了用户友好）
    AVCaptureVideoPreviewLayer *layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
    [self.captureBgView.layer insertSublayer:layer atIndex:0];
    layer.frame = self.captureBgView.layer.bounds;
    self.layer = layer;
    
    //（拍照部分）
    //图层展示拍摄角度方向
    layer.connection.videoOrientation = AVCaptureVideoOrientationPortrait;
    layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    
    
    //4、启动会话，让输入开始采集数据、输出对象、开始处理数据
//    dispatch_async(dispatch_get_global_queue(0, 0), ^{
//
//        [session startRunning];
//    });
    
    [session startRunning];
    
}

- (void)dealloc{
//    dispatch_async(dispatch_get_global_queue(0, 0), ^{
//
//        [self.session stopRunning];
//    });
    
    [self.session stopRunning];
}

//扫描到结果之后调用
-(void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
    
    /*
//    YWLog(@"%@",metadataObjects.description); //stringValue、corners、bounds、faceID有用
    //二维码结果格式：
//    (
//        "<AVMetadataMachineReadableCodeObject: 0x28177e260,\
//        type=\"org.iso.QRCode\",\
//        bounds={ 0.6,0.3 0.0x0.1 }>\
//        corners { 0.6,0.4 0.6,0.4 0.6,0.3 0.6,0.3 },\
//        time 350751084515375, \
//        stringValue \"I love you.\"",
//    )
    //人面结果格式：AVMetadataFaceObject
//    (
//        "<AVMetadataFaceObject: 0x280d685e0, faceID=1, bounds={0.3,0.6 0.1x0.1}, rollAngle=268.0, yawAngle=352.0, time=351286110340791>"
//    )
    
    */
    
    //移除之前添加的框框
    [self removeSubLayersFrame];
    
    /*
    //二维码
    for (id obj in metadataObjects) {
        if([obj isKindOfClass:[AVMetadataMachineReadableCodeObject class]]){
            
            //转换AVMetadataObject的视觉属性到图层坐标
            //转化成为二维码在预览图层上的真正坐标
            AVMetadataObject *resultObj = [self.layer transformedMetadataObjectForMetadataObject:obj];
            
            AVMetadataMachineReadableCodeObject *qrCodeObj = (AVMetadataMachineReadableCodeObject *)resultObj;
            
            YWLog(@"qrCodeObj、stringValue:%@、corners:%@",qrCodeObj.stringValue,qrCodeObj.corners.description);//corners 代表二维码的四个角、但是要借助视频预览图层转化成为我们需要可以用的坐标
           
            //画二维码框框
            [self drawQRCodeFrameWithQRCodeObj:qrCodeObj];
        }
    }
    */
    
    //人面
    for (id obj in metadataObjects) {
        if([obj isKindOfClass:[AVMetadataFaceObject class]]){
            
            //转换AVMetadataObject的视觉属性到图层坐标
            //转化成为人面在预览图层上的真正坐标
            AVMetadataObject *resultObj = [self.layer transformedMetadataObjectForMetadataObject:obj];
            
            AVMetadataFaceObject *faceObj = (AVMetadataFaceObject *)resultObj;
            
            YWLog(@"faceObj、bounds:%@",NSStringFromCGRect(faceObj.bounds));
            
            //画人面框框
            [self drawfaceObjFrameWithFaceObj:faceObj];
        }
    }
    
    //（拍照部分）
    //拍照片 （检测到人面就拍照片）
    [self takePhoto];
    
}

//（拍照部分）
//拍照片 （检测到人面就拍照片）
-(void)takePhoto{
    AVCaptureConnection * videoConnection = [self.imageOutPut connectionWithMediaType:AVMediaTypeVideo];
    if (videoConnection ==  nil) {
        return;
    }
    AVCapturePhotoSettings *set = [AVCapturePhotoSettings photoSettings];
    [self.imageOutPut capturePhotoWithSettings:set delegate:self];

}

//（拍照部分）
-(void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error {
    if (!error) {
        NSData *imageData = [photo fileDataRepresentation];
        UIImage *image = [UIImage imageWithData:imageData];
        
        
        YWLog(@"dd - 拍照成功")
        
        image = [self pressImage:image];//压缩图片防止内存过大而崩溃
        
        image = [self dealImage:image]; //系统拍照的照片含有方向属性所以要处理
        
        //通过拍照下来的照片来检测人面和裁剪成人像图片
        [self detectorImageAndTailorToDrawIcon:image];
        
    }
}

//通过拍照下来的照片来检测人面和裁剪成人像图片
-(void)detectorImageAndTailorToDrawIcon:(UIImage *)image{
    
    //移除scrollView上面的icon
    [self removeIconViewFromScrollView];
    
    // .1 获取需要识别的图片
    CIImage *imageCI = [[CIImage alloc] initWithImage:image];
    
    // 开始识别
    // 1. 创建一个二维码探测器
    CIDetector *dector = [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:@{CIDetectorAccuracy : CIDetectorAccuracyHigh}];
    
    // 2. 直接探测二维码特征
    NSArray *features = [dector featuresInImage:imageCI];
    
    for (CIFeature *feature in features) {
        CIFaceFeature *faceFeature = (CIFaceFeature *)feature;
        
        //添加人面icon到ScrollView
        [self addIconViewToScrollViewWithImage:image faceFeature:faceFeature];
    }
}



//添加人面icon
-(void)addIconViewToScrollViewWithImage:(UIImage *)image faceFeature:(CIFaceFeature *)faceFeature{
    
    //可以确定feature的size在转换坐标系、size不受坐标系影响
    CGSize faceFeatureSize = faceFeature.bounds.size;
    
    CGSize imageSize = image.size;
    
    //faceFeatureSize是确定了你要生成图片的大小
    UIGraphicsBeginImageContextWithOptions(faceFeatureSize, NO, 0);
    
    //裁剪区域就是裁剪的大小、所以是CGRectMake(0, 0, bounds.size.width, bounds.size.height)
    //把ImageV绘制到上下文之前,设置一个裁剪区域
    CGRect rect = CGRectMake(0, 0, faceFeatureSize.width, faceFeatureSize.height);
    UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:rect];//裁剪的是圆形
//    UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:rect];
    [clipPath addClip];
        
    
    //1、因为上下文不是整张image的size,而是很小的头像的size，这时假如你把上下文坐标系上下颠倒就把头像区域反转而已、所以我不能用转换坐标系方法、而是手动计算Y值
    
    //2、探测器CIDetector坐标的0点依然在左下角、所以X保持不变、Y要手动计算将距离下面Y轴间距变成距离上面Y轴间距
    
    CGRect faceFeatureBounds = faceFeature.bounds;
    
    //不能用上面的renderInContext、只能用drawAtPoint、且x和y是负数确定位置
    CGFloat tailorX = -faceFeatureBounds.origin.x;
    CGFloat y = imageSize.height - faceFeatureSize.height - faceFeatureBounds.origin.y;
    CGFloat tailorY = -y;
    CGPoint tailorPoint = CGPointMake(tailorX, tailorY);
    [image drawAtPoint:tailorPoint];
    
    
    //.从上下文当中生成 一张图片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    //关闭上下文
    UIGraphicsEndImageContext();
    
    // ---------------------------
    
    UIImageView *iconView = [[UIImageView alloc] init];
    iconView.image = newImage;
    CGFloat margin = 10;
    CGFloat height = self.iconBgView.height;
    CGFloat width = height;
    CGFloat iconY = 0;
    CGFloat iconX = margin + (margin + width) * self.iconScrollView.subviews.count;
    iconView.frame = CGRectMake(iconX, iconY, width, height);
    [self.iconScrollView addSubview:iconView];
    
}





//- - -- - - - -- -  - --- - --- -
//移除之前添加的框框
-(void)removeSubLayersFrame{
    
    NSArray *subLayers = self.layer.sublayers;
    
    //涉及数组的操作通过倒序遍历不会崩
    [subLayers enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if([obj isKindOfClass:[CAShapeLayer class]]){
            CAShapeLayer *subLayer = (CAShapeLayer *)obj;
            
            [subLayer removeFromSuperlayer];
            
        }
    }];
    
}

//画人面框
-(void)drawfaceObjFrameWithFaceObj:(AVMetadataFaceObject *)faceObj{
    
    CGRect bounds = faceObj.bounds;
    
    //1、借助一个图形层来绘制
    CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
    shapeLayer.fillColor = [UIColor clearColor].CGColor; // 填充颜色要清空
    shapeLayer.strokeColor = [UIColor redColor].CGColor; // 线条的颜色
    shapeLayer.lineWidth = 2; // 线宽
    
    //2、根据矩形来创建一个路径
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:bounds];
    
    //3、给图形图层的路径赋值、代表图层展示怎样的形状
    shapeLayer.path = path.CGPath;
    
    //4、直接添加图形图层到需要展示的图层
    [self.layer addSublayer:shapeLayer];
    
}

/*
//画二维码框
-(void)drawQRCodeFrameWithQRCodeObj:(AVMetadataMachineReadableCodeObject *)qrCodeObj{
    
    NSArray *corners = qrCodeObj.corners;
    
    //1、借助一个图形层来绘制
    CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
    shapeLayer.fillColor = [UIColor clearColor].CGColor; // 填充颜色要清空
    shapeLayer.strokeColor = [UIColor redColor].CGColor; // 线条的颜色
    shapeLayer.lineWidth = 2; // 线宽
    
    //2、根据四个点来创建一个路径
    UIBezierPath *path = [UIBezierPath bezierPath];
    NSInteger index = 0;
    for (id corner in corners) { //将字典转为CGPoint
        CFDictionaryRef pointDic = (__bridge CFDictionaryRef)corner;
        CGPoint point = CGPointZero;
        CGPointMakeWithDictionaryRepresentation(pointDic, &point);
        
       //根据点一条一条地画pat
        if(index == 0){  //第一个点
            
            [path moveToPoint:point];
        }
        else{
            
            [path addLineToPoint:point];
        }
        index ++;
    }
    
    [path closePath]; //给path封口
    
    //3、给图形图层的路径赋值、代表图层展示怎样的形状
    shapeLayer.path = path.CGPath;
    
    //4、直接添加图形图层到需要展示的图层
    [self.layer addSublayer:shapeLayer];
    
}
*/


// - ---- - - - - - - ---- --  -
-(void)removeScanAnimation{
    
    [self.chongjiboImageView.layer removeAllAnimations];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//    [self removeScanAnimation];
}


// ----------------------------------- - - -- - ---------- - -  --------------- - -

//移除scrollView上面的icon
-(void)removeIconViewFromScrollView{
    NSArray *iconArray = self.iconScrollView.subviews;
    [iconArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        UIImageView *icon = (UIImageView *)obj;
        [icon removeFromSuperview];
    }];
    
}

// ---------- -- -- -- --- -- -- -- - --- -- -------


//其它相机或者网络图片或者截屏什么的imageOrientation属性默认都是0、而而系统相机的image的imageOrientation有多个方向、所以导致人面检测画框位置不准
-(UIImage *)dealImage:(UIImage *)image{
    
    //要转为png (你不转png也行、不过你转了png后显示就能露出它的问题破绽)
    NSData *imageData = UIImagePNGRepresentation(image);
    
    UIImage *newImage = [UIImage imageWithData:imageData];
    
    YWLog(@"%ld",image.imageOrientation);
    YWLog(@"%ld",newImage.imageOrientation); //因为转为png后无论什么方向imageOrientation都变为0了

    switch (image.imageOrientation) { //通过原来的image判断方向
        case UIImageOrientationUp:
        {
            newImage = [self rotateImage:newImage rotation:0];
        }
            break;
        case UIImageOrientationDown:
        {
            
            newImage = [self rotateImage:newImage rotation:M_PI];
        }
            break;
        case UIImageOrientationLeft:
        {
            newImage = [self rotateImage:newImage rotation:M_PI_2];
        }
            break;
        case UIImageOrientationRight:
        {
            
            newImage = [self rotateImage:newImage rotation:-M_PI_2];
        }
            break;

        default:
            break;
    }
    
    return  newImage;
}

//这种强制转换方向也会导致人面检测画框位置不准:UIImage *rotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationUp];
//所以用下面的方法
-(UIImage *)rotateImage:(UIImage *)newImage rotation:(CGFloat)angle{
    
    CIImage *ciImage = [CIImage imageWithCGImage:newImage.CGImage];
    CGAffineTransform transform = CGAffineTransformMakeRotation(angle); //-M_PI_2
    CIImage *rotatedImage = [ciImage imageByApplyingTransform:transform];
    CGRect extent = [rotatedImage extent];
    CIContext *context = [CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer: @NO}];
    CGImageRef cgImage = [context createCGImage:rotatedImage fromRect:extent];
    UIImage *adjustedImage = [UIImage imageWithCGImage:cgImage];
    
    return  adjustedImage;
    
}

//压缩图片防止内存过大而崩溃
-(UIImage *)pressImage:(UIImage *)image{
    
    CGSize originalSize = image.size;
    CGFloat newWidth = 0;
    CGFloat newHeight = 0;
    
    if (originalSize.width > originalSize.height) {
        
        newWidth = 256.0;
        newHeight = originalSize.height / originalSize.width * newWidth;
    }else {
        
        newHeight = 256.0;
        newWidth = originalSize.width / originalSize.height * newHeight;
    }
    
    CGSize size = CGSizeMake(newWidth, newHeight);
    
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    
    
    //从上下文当中取出一张图片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    //关闭上下文
    UIGraphicsEndImageContext();
    
    return newImage;
}





@end
